﻿using FFmpeg.AutoGen;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;

namespace FFmpegWinui3.UlitFolder
{
    public unsafe class DecodecAudio
    {
        //    public string Url { get; private set; }

        //    AVCodecContext* oc;
        //    int audioIndex = -1;
        //    int outBufferSize;
        //    IntPtr outBuffer;
        //    SwrContext* audioConvert;
        //    //通道数
        //    int channels;
        //    //采样率
        //    int sampleRate;
        //    //输出的声道布局：立体声
        //    long outChLayout = ffmpeg.AV_CH_LAYOUT_STEREO;
        //    //输出的采样格式 16bit PCM
        //    AVSampleFormat outSampleFmt = AVSampleFormat.AV_SAMPLE_FMT_S16;

        //    AVFormatContext* ac;
        //    public AVPacket* packet;

        //    public DecodecAudio(string url)
        //    {
        //        Url = url;
        //        InitAudio();
        //    }
        //    public int InitAudio()
        //    {
        //        int error = 0;
        //        ac = ffmpeg.avformat_alloc_context();
        //        var tempAc = ac;
        //        error = ffmpeg.avformat_open_input(&tempAc, Url, null, null);
        //        if (error < 0)
        //            return error;

        //        error = ffmpeg.avformat_find_stream_info(ac, null);
        //        if (error < 0)
        //            return error;
        //        AVCodec* codec = null;
        //        audioIndex = ffmpeg.av_find_best_stream(ac, AVMediaType.AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);
        //        if (audioIndex < 0)
        //            return -1;
        //        oc= ffmpeg.avcodec_alloc_context3(codec);
        //        error = ffmpeg.avcodec_open2(oc, codec, null);
        //        if (error < 0)
        //            return error;

        //        var stream = ac->streams[audioIndex];

        //        //输入的音频采样格式
        //        var inSampleFmt = oc->sample_fmt;
        //        //输入的采样率
        //        sampleRate = stream->codecpar->sample_rate;
        //        var inChLayout = stream->codecpar-> channel_layout;

        //        audioConvert = ffmpeg.swr_alloc();
        //        ffmpeg.swr_alloc_set_opts(audioConvert, outChLayout, outSampleFmt, sampleRate, (long)inChLayout, inSampleFmt, sampleRate, 0, null);
        //        ffmpeg.swr_init(audioConvert);

        //        channels = ffmpeg.av_get_channel_layout_nb_channels((ulong)outChLayout);
        //        outBuffer = Marshal.AllocHGlobal(sampleRate * 2);
        //        packet = ffmpeg.av_packet_alloc();
        //        return error;
        //    }


        //    public bool TryReadNextFrame(out AVFrame frame)
        //    {

        //        AVFrame* videoFrame = ffmpeg.av_frame_alloc();
        //        frame = *videoFrame;
        //        int error = 0;
        //        do
        //        {

        //            ffmpeg.av_packet_unref(packet);
        //            error = ffmpeg.av_read_frame(ac, packet);
        //            if (ffmpeg.AVERROR_EOF == error)
        //            {
        //                frame = *videoFrame;
        //                return false;
        //            }

        //        } while (packet->stream_index != audioIndex);

        //        error = ffmpeg.avcodec_send_packet(oc, packet);
        //        ffmpeg.av_packet_unref(packet);
        //        ffmpeg.avcodec_receive_frame(oc, videoFrame);
        //        frame = *videoFrame;


        //        return true;
        //    }

        //    public byte[] AudioConvert(AVFrame* audioFrame)
        //    {
        //        var outputBufferPtr = (byte*)outBuffer;



        //       var bl= ffmpeg.av_samples_get_buffer_size(null, channels, audioFrame->nb_samples, AVSampleFormat.AV_SAMPLE_FMT_S16, 1);

        //        var by= Marshal.AllocHGlobal(bl);
        //        var aa = (byte*)by;

        //        ffmpeg.swr_convert(audioConvert, &aa, sampleRate * 2, audioFrame->extended_data, audioFrame->nb_samples);

        //        var bytes = new byte[bl];
        //        Marshal.Copy(by,bytes,0,bl); 
        //        return bytes;
        //    }
        //}


        // FFmpegDll.cpp: 定义控制台应用程序的入口点。
        //


        AVFormatContext* fmt_ctx;

        //流队列中，视频流所在的位置
        int video_index = -1;
        int audio_index = -1;

        //解码上下文
        AVCodecContext* video_codec_ctx;
        AVCodecContext* audio_codec_ctx;

        //输出缓存大小
        int video_out_buffer_size;
        int audio_out_buffer_size;

        //输出缓存
        IntPtr video_out_buffer;
        IntPtr audio_out_buffer;

        //转码后输出的视频帧（如yuv转rgb24）
        AVFrame* video_out_frame = ffmpeg.av_frame_alloc();

        //格式转换上下文
        SwsContext* video_convert_ctx;
        SwrContext* audio_convert_ctx;

        //解码前数据包
        AVPacket* packet = ffmpeg.av_packet_alloc();

        //音频声道数量
        int nb_channels;

        AVSampleFormat out_sample_fmt = AVSampleFormat.AV_SAMPLE_FMT_S16;//输出的采样格式 16bit PCM
        int sample_rate;//采样率
        long out_ch_layout = ffmpeg.AV_CH_LAYOUT_STEREO;//输出的声道布局：立体声

        //初始化FFmpeg 
        //@param *url 媒体地址（本地/网络地址）
        public int init_ffmpeg(string url)
        {

            fmt_ctx = ffmpeg.avformat_alloc_context();
            var temp = fmt_ctx;
            //打开文件
            if (ffmpeg.avformat_open_input(&temp, url, null, null) != 0)
            {
                return -1;
            }

            //查找流信息
            if (ffmpeg.avformat_find_stream_info(fmt_ctx, null) < 0)
            {
                return -1;
            }

            //找到流队列中，视频流所在位置
            for (int i = 0; i < fmt_ctx->nb_streams; i++)
            {
                if (fmt_ctx->streams[i]->codecpar->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
                {
                    video_index = i;
                }
                if (fmt_ctx->streams[i]->codecpar->codec_type == AVMediaType.AVMEDIA_TYPE_AUDIO)
                {
                    audio_index = i;
                }
            }

            //音视频流都没有找到
            if (video_index == -1 && audio_index == -1)
            {
                return -1;
            }

            //查找解码器
            video_codec_ctx = ffmpeg.avcodec_alloc_context3(null);
            audio_codec_ctx = ffmpeg.avcodec_alloc_context3(null);
            AVCodec* video_codec = null;
            AVCodec* audio_codec = null;
            if (video_index >= 0)
                video_codec = ffmpeg.avcodec_find_decoder(fmt_ctx->streams[video_index]->codecpar->codec_id);
            if (audio_index >= 0)
                audio_codec = ffmpeg.avcodec_find_decoder(fmt_ctx->streams[audio_index]->codecpar->codec_id);

            //解码器没有找到
            if (video_codec == null && audio_codec == null)
            {
                return -1;
            }

            //打开解码器
            if (ffmpeg.avcodec_open2(video_codec_ctx, video_codec, null) < 0 & //两个都要打开，用&
                ffmpeg.avcodec_open2(audio_codec_ctx, audio_codec, null) < 0)
            {
                return -1;
            }

            //======视频转码准备======start======
            //计算输出缓存
            //video_out_buffer_size = ffmpeg.av_image_get_buffer_size(
            //    AVPixelFormat.AV_PIX_FMT_RGB24,
            //    video_codec_ctx->width,
            //    video_codec_ctx->height,
            //    1);

            ////输出缓存
            //video_out_buffer = Marshal.AllocHGlobal(video_out_buffer_size);
            //var _dstData = new byte_ptrArray4();
            //var _dstLinesize = new int_array4();
            ////准备一些参数，在视频格式转换后，参数将被设置值
            //ffmpeg.av_image_fill_arrays(
            //    ref _dstData,//转换后的数据
            //   ref _dstLinesize,
            //   (byte*)video_out_buffer, //视频buffer
            //   AVPixelFormat.AV_PIX_FMT_RGB24,//像素格式
            //    video_codec_ctx->width,
            //    video_codec_ctx->height,
            //    1);

            //video_convert_ctx = ffmpeg.sws_getContext(//图片格式转换上下文
            //    video_codec_ctx->width,
            //    video_codec_ctx->height,
            //    video_codec_ctx->pix_fmt,
            //    video_codec_ctx->width,
            //    video_codec_ctx->height,
            //    AVPixelFormat.AV_PIX_FMT_RGB24,//转码为RGB像素
            //    ffmpeg.SWS_BICUBIC,
            //    null, null, null);
            //======视频转码准备======end======

            //======音频转码准备======start======
            AVSampleFormat in_sample_fmt = audio_codec_ctx->sample_fmt;//输入的采样格式
            sample_rate = audio_codec_ctx->sample_rate;//输入的采样率
            ulong in_ch_layout = audio_codec_ctx->channel_layout;//输入的声道布局

            audio_convert_ctx = ffmpeg.swr_alloc();
            ffmpeg.swr_alloc_set_opts(audio_convert_ctx, out_ch_layout, out_sample_fmt, sample_rate, (long)in_ch_layout, in_sample_fmt, sample_rate, 0, null);
            ffmpeg.swr_init(audio_convert_ctx);

            nb_channels = ffmpeg.av_get_channel_layout_nb_channels((ulong)out_ch_layout);//获取声道个数
            audio_out_buffer = Marshal.AllocHGlobal(sample_rate * 2);//存储pcm数据
                                                                     //======音频转码准备======end======

            ffmpeg.av_init_packet(packet);
            return 0;
        }

        //读取一帧 -1：未取到 1：音频 2：视频 
        public int read_frame()
        {
            int ret = -1;

            //是否从packet中解出一帧，0为未解出
            int got_picture;
            int got_frame;

            //从packet中解出来的原始音视频帧
            AVFrame* original_audio_frame = ffmpeg.av_frame_alloc();
            AVFrame* original_video_frame = ffmpeg.av_frame_alloc();

            if (ffmpeg.av_read_frame(fmt_ctx, packet) == 0)
            {
                if (packet->stream_index == video_index)
                {
                    //解码。输入为packet，输出为original_video_frame
                    //if (ffmpeg.avcodec_decode_video2(video_codec_ctx, original_video_frame, &got_picture, packet) >= 0)
                    //{
                    //if (got_picture > 0)
                    //{
                    //图片格式转换（上面图片转换准备的参数，在这里使用）
                    //    ffmpeg.sws_scale(video_convert_ctx,//图片转码上下文
                    //        original_video_frame->data,//原始数据
                    //original_video_frame->linesize,//原始参数
                    //0,//转码开始游标，一般为0
                    //video_codec_ctx->height,//行数
                    //video_out_frame->data,//转码后的数据
                    //video_out_frame->linesize);
                    //    ret = 2;
                    //转码后，将buffer拷贝到非托管区内存，其他地方可以读取该内存；
                    //memcpy(video_buffer, video_out_buffer, video_out_buffer_size);
                    //}
                    //}
                }
                else if (packet->stream_index == audio_index)
                {
                    ffmpeg.avcodec_send_packet(audio_codec_ctx, packet);
                    ffmpeg.avcodec_receive_frame(audio_codec_ctx, original_audio_frame);

                    //if (avcodec_decode_audio4(audio_codec_ctx, original_audio_frame, &got_frame, packet) >= 0)
                    //{
                    //if (got_frame > 0)
                    //{
                    var audioOutByter = (byte*)audio_out_buffer;
                    //音频格式转换
                    ffmpeg.swr_convert(audio_convert_ctx,//音频转换上下文
                        &audioOutByter,//输出缓存
                        sample_rate * 2,//每次输出大小
                        original_audio_frame->extended_data,//输入数据
                original_audio_frame->nb_samples);//输入

                    audio_out_buffer_size = ffmpeg.av_samples_get_buffer_size(null, nb_channels, original_audio_frame->nb_samples, out_sample_fmt, 1);
                    ret = 1;
                    //}
                    //}
                }


            }

            ffmpeg.av_packet_unref(packet);
            ffmpeg.av_free(original_audio_frame);
            ffmpeg.av_free(original_video_frame);
            return ret;
        }

        //获取音频缓存大小
        public int get_audio_buffer_size()
        {
            return audio_out_buffer_size;
        }

        //获取视频缓存大小
        public int get_video_buffer_size()
        {
            return video_out_buffer_size;
        }

        //获取音频帧
        public IntPtr get_audio_frame()
        {
            return audio_out_buffer;
        }

        //获取视频帧
        public IntPtr get_video_frame()
        {
            return video_out_buffer;
        }

        //获取视频宽度
        public int get_video_width()
        {
            return video_codec_ctx->width;
        }

        //获取视频高度
        public int get_video_height()
        {
            return video_codec_ctx->height;
        }

        //释放资源
        public void release()
        {
            ffmpeg.sws_freeContext(video_convert_ctx);
            ffmpeg.swr_close(audio_convert_ctx);

            ffmpeg.av_free(video_out_frame);

            Marshal.FreeHGlobal(video_out_buffer);
            Marshal.FreeHGlobal(audio_out_buffer);

            ffmpeg.avcodec_close(video_codec_ctx);
            ffmpeg.avcodec_close(audio_codec_ctx);

            ffmpeg.avformat_free_context(fmt_ctx);
        }


        public int main()
        {
            //char url[] = "rtmp://live.hkstv.hk.lxdns.com/live/hks";
            string url = "";
            int init_ret = init_ffmpeg(url);
            if (init_ret >= 0)
            {
                int read = -1;
                while ((read = read_frame()) != 1)//读取一帧，直到读取到数据
                {
                    Debug.WriteLine("未读取到数据\n");
                }
                //Debug.WriteLine("高度:%d  宽度:%d  缓存大小:%d", get_video_height(), get_video_width(), get_video_buffer_size());
                //Debug.WriteLine("\n=================================\n");
                //Debug.WriteLine(get_video_frame());//打印出来，虽然打印出来的东西看不懂，但是证明已经获取到一帧的数据了

                while ((read = read_frame()) > 0)//循环读取音频
                {
                    if (read == 1)
                    {
                        Debug.WriteLine("缓存大小:%d", get_audio_buffer_size());
                        Debug.WriteLine("\n=================================\n");
                        //Debug.WriteLine(get_audio_frame().);
                        Debug.WriteLine("\n=================================\n");
                    }
                }
            }
            else
            {
                Debug.WriteLine("初始化失败!");
            }
            //getchar();
            return 0;
        }

    }
}
